﻿//Copyright (c) 2008 Michael J. Ryan & Apollo Group, Inc.

//Permission is hereby granted, free of charge, to any person
//obtaining a copy of this software and associated documentation
//files (the "Software"), to deal in the Software without
//restriction, including without limitation the rights to use,
//copy, modify, merge, publish, distribute, sublicense, and/or sell
//copies of the Software, and to permit persons to whom the
//Software is furnished to do so, subject to the following
//conditions:

//The above copyright notice and this permission notice shall be
//included in all copies or substantial portions of the Software.

//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
//EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
//OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
//NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
//HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
//WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
//FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
//OTHER DEALINGS IN THE SOFTWARE.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;

namespace Apollo.Common.Configuration
{
	public class ConfigurationItem
	{
		private List<string> _values = new List<string>();
		private XElement _elem;
		private string _name;
	
		internal ConfigurationItem() {}
		internal ConfigurationItem(XElement item) {	
			_elem = item;
			_name = _elem.Attribute("name").Value.Trim().ToLower();
		}
	
		public string Value
		{
			get
			{
				LazyLoad();
				if (_values.Count == 0) return null;
				if (_values.Count == 1) return _values[0];
				return string.Join(";", _values.ToArray());
			}
		}
		
		public string[] Values
		{
			get {
				return GetValues();
			}
		}
		
		public string Name
		{
			get
			{
				return _name;
			}
		}
				
		public T GetValue<T>()
		{
			return GetValue<T>(default(T));
		}
		
		public T GetValue<T>(T defaultValue)
		{
			LazyLoad();
			string s = Value;
			
			if (s == null)
				return defaultValue;
			
			Dictionary<Type, Func<object>> types = new Dictionary<Type,Func<object>>();
			types.Add(typeof(byte), () => int.Parse(s));
			types.Add(typeof(sbyte), () => sbyte.Parse(s));
			types.Add(typeof(short), () => short.Parse(s));
			types.Add(typeof(ushort), () => ushort.Parse(s));
			types.Add(typeof(int), () => int.Parse(s));
			types.Add(typeof(uint), () => uint.Parse(s));
			types.Add(typeof(long), () => long.Parse(s));
			types.Add(typeof(ulong), () => ulong.Parse(s));
			types.Add(typeof(float), () => float.Parse(s));
			types.Add(typeof(double), () => double.Parse(s));
			types.Add(typeof(decimal), () => decimal.Parse(s));
			types.Add(typeof(DateTime), () => DateTime.Parse(s));
			types.Add(typeof(DateTimeOffset), () => DateTimeOffset.Parse(s));
			types.Add(typeof(TimeSpan), () => TimeSpan.Parse(s));
			
			Func<object> getter;
			types.TryGetValue(typeof(T), out getter);
			
			try
			{
				if (getter != null)
					return (T)getter();
					
				return ((T)(s as object)); //try direct cast
			}
			catch (InvalidCastException err)
			{
				LogTool.Logger.ErrorException(
					string.Format(
						"Apollo.Common.Configuration.ConfigurationItem Invalid cast to T ({0}) from string ({1})",
						typeof(T),
						s
					), 
					err
				);
				throw new InvalidCastException(
					string.Format(
						"Unable to case to typeof({0}) from string (\"{1}\").",
						typeof(T),
						s
					), 
					err
				);
			}
		}
		
		public string[] GetValues()
		{
			LazyLoad();
			return _values.ToArray(); //duplicate the list.
		}
		
		private void LazyLoad()
		{
			if (_elem == null)
				return;
				
			lock(_values) {
				if (_values.Count > 0)
					return; //already loaded
					
				List<string> vals = new List<string>();
				
				if (_elem.Attribute("value") != null) {
					vals.Add(_elem.Attribute("value").Value);
				}
				else
				{
					//test for children
					List<XElement> vx =	(
											from v in _elem.Descendants("value")
											select v
										).ToList();
					
					if (vx.Count > 0) {
						//use children
						foreach (XElement v in vx)
							vals.Add(v.Value.Trim());
							
					} else {
						//use string for value
						vals.Add(_elem.Value);
												
					}
				}
				
				_values = vals;
			}
		}
	
	}
}
